package mallindex

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/jwt"
	"mall/common/utils/pointy"
	"mall/service/blog/rpc/blogclient"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/api/internal/utils/parse"
	"mall/service/forum/rpc/forumclient"
	"mall/service/product/rpc/productclient"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/mr"
)

type GetSearchResultLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetSearchResultLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSearchResultLogic {
	return &GetSearchResultLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetSearchResultLogic) GetSearchResult(
	req *types.GetSearchResultReq,
	headers *[]*types.Header,
) (*types.GetSearchResultResp, error) {
	userId := jwt.GetUserId(l.ctx)

	platformId := globalkey.ThirdPartyPlatformWechatMini

	resp := &types.GetSearchResultResp{}

	var keyword string
	if req.Keyword == nil || len(strings.TrimSpace(*req.Keyword)) < 2 {
		return resp, nil
	}

	keyword = strings.TrimSpace(*req.Keyword)
	keywordRune := []rune(keyword)
	if len(keywordRune) > 38 { // 截取部分，防止恶意攻击
		keyword = string(keywordRune[0:38])
		resp.Ignored = pointy.GetPointer(true)
		resp.ActualKeyword = pointy.GetPointer(keyword)
	}
	var reqTab uint32
	if req.Tab != nil {
		reqTab = *req.Tab
	}
	var preFilter string
	if req.Type != nil {
		preFilter = *req.Type
	}

	// var sorter string
	// if req.Sorter != nil {
	// 	sorter = strings.TrimSpace(*req.Sorter)
	// 	switch sorter {
	// 	case "goods_new":
	// 		sorter = "CreatedAt:1;"
	// 	case "goods_price_desc":
	// 		sorter = "RetailPrice:1;"
	// 	case "goods_price_asc":
	// 		sorter = "RetailPrice:0;"
	// 	}
	// }

	page, pageSize := parse.ParsePageInfo(
		req.Current,
		req.PageSize,
		l.svcCtx.Config.Query.DefaultAmount,
		l.svcCtx.Config.Query.MaxAmount,
	)
	var hasResult bool
	err := mr.Finish(func() error {
		goodsList, err := l.getGoodsList(reqTab, page, pageSize, userId, platformId, keyword, preFilter)
		if goodsList != nil {
			hasResult = true
			resp.GoodsList = goodsList
		}

		return err
	}, func() error {
		entryList, err := l.getEntryList(reqTab, page, pageSize, userId, platformId, keyword, preFilter)
		if entryList != nil {
			hasResult = true
			resp.EntryList = entryList
		}

		return err
	}, func() error {
		reviewList, err := l.getReviewList(reqTab, page, pageSize, userId, platformId, keyword, preFilter)
		if reviewList != nil {
			hasResult = true
			resp.ReviewList = reviewList
		}

		return err
	}, func() error {
		blogPostList, err := l.getBlogPostList(reqTab, page, pageSize, userId, platformId, keyword, preFilter)
		if blogPostList != nil {
			hasResult = true
			resp.BlogPostList = blogPostList
		}

		return err
	})

	if err != nil {
		fmt.Println("./...////", err)
		return nil, err
	}

	resp.HasResult = pointy.GetPointer(hasResult)

	// Todo: 记录搜索记录
	// if page == 1 && len(keyword) > 0 {
	// 	logRes, err := l.svcCtx.ProductRpc.SearchLog(l.ctx, &productclient.SearchLogReq{
	// 		Keyword:    pointy.GetPointer(keyword),
	// 		VisitorId:  pointy.GetPointer(userId),
	// 		PlatformId: pointy.GetPointer(platformId),
	// 	})
	// 	if err != nil {
	// 		logrus.Info(fmt.Sprintf("GetProductList SearchLosg err: %+v", err))
	// 	} else {
	// 		resp.SearchId = pointy.GetPointer(logRes.Id)
	// 	}
	// }
	fmt.Println("ressplkl[[[")
	return resp, nil
}

func (l *GetSearchResultLogic) getGoodsList(
	reqTab uint32,
	page uint64,
	pageSize uint64,
	userId uint64,
	platformId uint32,
	keyword string,
	preFilter string, // Todo: 上新，价格排序
) (resp *types.GoodsListResp, err error) {
	if reqTab != 0 && reqTab != 1 {
		return nil, nil
	}

	preload := "product:Id,Title,Slug"
	eagerLoad := "product:productMeta,productMedia"
	preload += ";productMeta:mediaUrl,retailPriceMin,hotSell"
	preload += ";productMedia:Uid,Url"
	var filters []string
	filters = append(filters, "virtual,equalTo:"+strconv.FormatInt(int64(globalkey.ProductTypePhysical), 10))
	filters = append(filters, "CategoryId,notEqualTo:"+strconv.FormatInt(l.svcCtx.Config.Query.TesterCategoryId, 10))
	filters = append(filters, "Status,equalTo:"+strconv.FormatInt(int64(globalkey.StatusEnabled), 10))

	res, err := l.svcCtx.ProductRpc.GetProductList(l.ctx, &productclient.BaseListReq{
		Offset:     uint32((page - 1) * pageSize),
		Limit:      uint32(pageSize),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		Search:     pointy.GetPointer(keyword),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
	})
	if err != nil {
		return nil, err
	}

	if len(res.List) == 0 {
		return nil, nil
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}

	resp = &types.GoodsListResp{
		BaseListInfo: types.BaseListInfo{
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	for _, v := range res.List {
		var retailPriceMin *string
		var couterPriceMin *string
		var isNew bool
		var isHot bool
		// var mediaId *uint64
		var mediaUrl *string
		if len(v.Meta) > 0 {
			for _, m := range v.Meta {
				if m.Key == nil || m.Value == nil {
					continue
				}

				val := *m.Value
				switch *m.Key {
				case "retailPriceMin":
					retailPriceMin = pointy.GetPointer(val)
				case "couterPriceMin":
					couterPriceMin = pointy.GetPointer(val)
				case "isNew":
					if val == "1" {
						isNew = true
					}
				case "isHot":
					if val == "1" {
						isHot = true
					}
				case "hotSell":
					if val == "1" {
						isHot = true
					}
				case "mediaUrl":
					mediaUrl = pointy.GetPointer(val)
				}
			}
		}

		if len(v.Media) > 0 {
			for _, m := range v.Media {
				if mediaUrl != nil {
					break
				}

				var url string
				if m.Url != nil && len(*m.Url) > 0 {
					url = *m.Url
				}
				if len(url) == 0 && m.Uri != nil && len(*m.Uri) > 0 {
					url = *m.Uri
				}
				mediaUrl = pointy.GetPointer(url)
			}
		}

		var preTag *string
		if isHot {
			preTag = pointy.GetPointer("热卖")
		} else if isNew {
			preTag = pointy.GetPointer("新品")
		}

		resp.List = append(resp.List, &types.GoodsInfo{
			Id:              v.Id,
			PreTag:          preTag,
			Title:           v.Title,
			Slug:            v.Slug,
			MinPrice:        retailPriceMin,
			CounterPriceMin: couterPriceMin,
			ImageUrl:        mediaUrl,
		})
	}

	return resp, nil
}

func (l *GetSearchResultLogic) getEntryList(
	reqTab uint32,
	page uint64,
	pageSize uint64,
	userId uint64,
	platformId uint32,
	keyword string,
	filter string, // Todo:
) (resp *types.GetEntryListResp, err error) {
	if reqTab != 0 && reqTab != 3 {
		return nil, nil
	}

	preload := "blogBook:Id,Title,Slug,SectionId"
	eagerLoad := "blogBook:blogBookMeta,blogMedia"
	preload += ";blogBookMeta:mediaUrl"
	preload += ";blogMedia:Uid,Url"
	filters := []string{"Status,equalTo:" + strconv.FormatInt(int64(globalkey.PostStatusPublish), 10)}
	if len(filter) > 0 {
		filters = append(filters, "SectionId,equalTo:"+filter)
	}

	res, err := l.svcCtx.BlogRpc.GetKBPostListTmp(l.ctx, &blogclient.BaseListReq{
		Offset:     uint32((page - 1) * pageSize),
		Limit:      uint32(pageSize),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		Search:     pointy.GetPointer(keyword),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
	})
	if err != nil {
		return nil, err
	}

	if len(res.List) == 0 {
		return nil, nil
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}

	resp = &types.GetEntryListResp{
		BaseListInfo: types.BaseListInfo{
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	if len(res.List) > 0 {
		for _, v := range res.List {
			var mediaUrl *string
			var reviewCount *uint32
			if len(v.Meta) > 0 {
				for _, m := range v.Meta {
					if m.Key == nil || m.Value == nil {
						continue
					}

					val := *m.Value
					switch *m.Key {
					case "mediaUrl":
						mediaUrl = pointy.GetPointer(val)
					case "reviewCount":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							reviewCount = pointy.GetPointer(uint32(i))
						}
					}
				}
			}

			if len(v.Media) > 0 {
				for _, m := range v.Media {
					if mediaUrl != nil {
						break
					}

					var url string
					if m.Url != nil && len(*m.Url) > 0 {
						url = *m.Url
					}
					if len(url) == 0 && m.Uri != nil && len(*m.Uri) > 0 {
						url = *m.Uri
					}
					mediaUrl = pointy.GetPointer(url)
				}
			}

			resp.List = append(resp.List, &types.KbEntryInfo{
				Id:          v.Id,
				Title:       v.Title,
				Slug:        v.Slug,
				ImageUrl:    mediaUrl,
				ReviewCount: reviewCount,
				SectionId:   v.SectionId,
			})
		}
	}

	return resp, nil
}

// 香评和前台文章
func (l *GetSearchResultLogic) getReviewList(
	reqTab uint32,
	page uint64,
	pageSize uint64,
	userId uint64,
	platformId uint32,
	keyword string,
	preFilter string,
) (resp *types.GetReviewListResp, err error) {
	if reqTab != 0 && reqTab != 2 {
		return nil, nil
	}

	preload := "forumPost:Id,Title,AuthorId,AuthorName,AuthorAvatar,AuthorRankId,AuthorRoleId,PublicDate"
	eagerLoad := "forumPost:forumPostMeta"
	preload += ";forumPostMeta:mediaUrl"
	var filters []string
	filters = append(filters, "Status,equalTo:"+strconv.FormatInt(int64(globalkey.StatusApproved), 10))
	filters = append(filters, "ForumId,in:"+strconv.FormatInt(l.svcCtx.Config.Query.ColumnForumId, 10)+","+strconv.FormatInt(l.svcCtx.Config.Query.PostForumId, 10))

	res, err := l.svcCtx.ForumRpc.GetPostList(l.ctx, &forumclient.BaseListReq{
		Offset:     uint32((page - 1) * pageSize),
		Limit:      uint32(pageSize),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
		Search:     pointy.GetPointer(keyword),
	})
	if err != nil {
		return nil, err
	}
	if len(res.List) == 0 {
		return nil, nil
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}

	resp = &types.GetReviewListResp{
		BaseListInfo: types.BaseListInfo{
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	for _, v := range res.List {
		var mediaUrl *string
		if len(v.Meta) > 0 {
			for _, m := range v.Meta {
				if m.Key == nil || m.Value == nil {
					continue
				}

				val := *m.Value
				switch *m.Key {
				case "mediaUrl":
					mediaUrl = pointy.GetPointer(val)
				}
			}
		}

		resp.List = append(resp.List, &types.ReviewInfo{
			Id:             v.Id,
			Title:          v.PostTitle,
			AuthorId:       v.AuthorId,
			AuthorNickname: v.AuthorNickname,
			AuthorAvatar:   v.AuthorAvatar,
			AuthorRankId:   v.AuthorRankId,
			AuthorRoleId:   v.AuthorRoleId,
			PublicDate:     v.PublicDate,
			ImageUrl:       mediaUrl,
		})
	}

	return resp, nil
}

// 后台文章
func (l *GetSearchResultLogic) getBlogPostList(
	reqTab uint32,
	page uint64,
	pageSize uint64,
	userId uint64,
	platformId uint32,
	keyword string,
	preFilter string,
) (resp *types.TmpPostListResp, err error) {
	if reqTab != 0 && reqTab != 2 {
		return nil, nil
	}

	preload := "blogPost:Id,AuthorId,Title,Excerpt,Status,CommentStatus,Slug,Uri,Link,Date,Modified,ContentHtml"
	eagerLoad := "blogPost:"
	var filters []string
	filters = append(filters, "Status,equalTo:"+strconv.FormatInt(int64(globalkey.StatusApproved), 10))

	res, err := l.svcCtx.BlogRpc.GetPostListTmp(l.ctx, &blogclient.BaseListReq{
		Offset:     uint32((page - 1) * pageSize),
		Limit:      uint32(pageSize),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
		Search:     pointy.GetPointer(keyword),
	})
	if err != nil {
		return nil, err
	}

	if len(res.List) == 0 {
		return nil, nil
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}

	resp = &types.TmpPostListResp{
		BaseListInfo: types.BaseListInfo{
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	for _, v := range res.List {
		var mediaUrl *string
		if len(v.Meta) > 0 {
			for _, m := range v.Meta {
				if m.Key == nil || m.Value == nil {
					continue
				}

				val := *m.Value
				switch *m.Key {
				case "_thumbnail_url":
					mediaUrl = pointy.GetPointer(val)
				}
			}
		}

		resp.List = append(resp.List, &types.TmpBlogPostInfo{
			BaseIDInfo: types.BaseIDInfo{
				Id: v.Id,
			},
			Title:    v.Title,
			Slug:     v.Slug,
			AuthorId: v.AuthorId,
			Status:   v.Status,
			Url:      mediaUrl,
		})
	}

	return resp, nil
}
